package com.bj58.ecdata.dao.util;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

import com.bj58.ecdata.dao.annotation.Column;
import com.bj58.ecdata.dao.annotation.Id;
import com.bj58.ecdata.dao.annotation.NotDBColumn;
import com.bj58.ecdata.dao.annotation.ProcedureName;
import com.bj58.ecdata.dao.annotation.Table;
import com.bj58.ecdata.dao.annotation.TableRename;
import com.google.common.collect.Maps;

class ClassInfo
{
  private Map<String, Field> mapIDField;
  private Map<String, Field> mapAllDBField;
  private Map<String, Field> mapInsertableField;
  private Map<String, Field> mapUpdatableField;
  private Map<String, Field> mapIdentityField;
  private Map<String, String> mapDBColumnName;
  private Map<String, Method> mapSetMethod;
  private Map<String, Method> mapGetMethod;
  private Map<String, Field> mapTableRenameField;
  private Map<String, Method> mapTableRenameSetMethod;
  private Map<String, Method> mapTableRenameGetMethod;
  private String tableName;
  private ProcedureName procdure;
  
  public ClassInfo(Class<?> clazz)
    throws Exception
  {
    setTableName(getTableName(clazz));
    setMapIDField(getIdFields(clazz));
    setMapAllDBField(getAllDBFields(clazz));
    setMapInsertableField(getInsertableFields(clazz));
    setMapUpdatableField(getUpdatableFields(clazz));
    setMapDBColumnName(getCloumnName(clazz));
    setMapSetMethod(getSetterMethod(clazz));
    setMapGetMethod(getGetterMethod(clazz));
    setProcdure(getProc(clazz));
    setMapIdentityField(getIdentityFields(clazz));
    
    setMapTableRenameField(getTableRenameFields(clazz));
    setMapTableRenameGetMethod(getTableRenameGetterMethod(clazz));
    setMapTableRenameSetMethod(getTableRenameSetterMethod(clazz));
  }
  
  private String getTableName(Class<?> clazz)
  {
    if (clazz.isAnnotationPresent(Table.class))
    {
      Table table = (Table)clazz.getAnnotation(Table.class);
      if (!table.name().equalsIgnoreCase("className")) {
        return table.name();
      }
    }
    String name = clazz.getName();
    return name.substring(name.lastIndexOf(".") + 1);
  }
  
  private Map<String, Field> getInsertableFields(Class<?> clazz)
  {
    Map<String, Field> mapFfields = Maps.newHashMap();
    Field[] fields = clazz.getDeclaredFields();
    Field[] arrayOfField1;
    int j = (arrayOfField1 = fields).length;
    for (int i = 0; i < j; i++)
    {
      Field f = arrayOfField1[i];
      if (!f.isAnnotationPresent(NotDBColumn.class)) {
        if (f.isAnnotationPresent(Id.class))
        {
          Id id = (Id)f.getAnnotation(Id.class);
          if (id.insertable()) {
            mapFfields.put(f.getName(), f);
          }
        }
        else
        {
          Column column = (Column)f.getAnnotation(Column.class);
          if (column != null)
          {
            if (!column.defaultDBValue()) {
              mapFfields.put(f.getName(), f);
            }
          }
          else {
            mapFfields.put(f.getName(), f);
          }
        }
      }
    }
    return mapFfields;
  }
  
  private Map<String, Field> getAllDBFields(Class<?> clazz)
  {
    Map<String, Field> mapFfields = Maps.newHashMap();
    Field[] fields = clazz.getDeclaredFields();
    Field[] arrayOfField1;
    int j = (arrayOfField1 = fields).length;
    for (int i = 0; i < j; i++)
    {
      Field f = arrayOfField1[i];
      if ((!f.isAnnotationPresent(NotDBColumn.class)) && 
        (!"serialVersionUID".equalsIgnoreCase(f.getName()))) {
        mapFfields.put(f.getName(), f);
      }
    }
    return mapFfields;
  }
  
  private Map<String, Field> getIdFields(Class<?> clazz)
  {
    Map<String, Field> mapFfields = Maps.newHashMap();
    Field[] fields = clazz.getDeclaredFields();
    Field[] arrayOfField1;
    int j = (arrayOfField1 = fields).length;
    for (int i = 0; i < j; i++)
    {
      Field f = arrayOfField1[i];
      if (f.isAnnotationPresent(Id.class)) {
        mapFfields.put(f.getName(), f);
      }
    }
    return mapFfields;
  }
  
  private Map<String, Field> getUpdatableFields(Class<?> clazz)
  {
    Map<String, Field> mapFfields = Maps.newHashMap();
    Field[] fields = clazz.getDeclaredFields();
    Field[] arrayOfField1;
    int j = (arrayOfField1 = fields).length;
    for (int i = 0; i < j; i++)
    {
      Field f = arrayOfField1[i];
      if (!f.isAnnotationPresent(NotDBColumn.class)) {
        if (f.isAnnotationPresent(Id.class))
        {
          Id id = (Id)f.getAnnotation(Id.class);
          if (id.updatable()) {
            mapFfields.put(f.getName(), f);
          }
        }
        else
        {
          Column column = (Column)f.getAnnotation(Column.class);
          if (column != null)
          {
            if (!column.defaultDBValue()) {
              mapFfields.put(f.getName(), f);
            }
          }
          else {
            mapFfields.put(f.getName(), f);
          }
        }
      }
    }
    return mapFfields;
  }
  
  private Map<String, String> getCloumnName(Class<?> clazz)
  {
    Map<String, String> mapNames = Maps.newHashMap();
    Collection<Field> fList = this.mapAllDBField.values();
    for (Field f : fList) {
      if (f.isAnnotationPresent(Column.class))
      {
        Column column = (Column)f.getAnnotation(Column.class);
        if (!column.name().equalsIgnoreCase("fieldName")) {
          mapNames.put(f.getName(), column.name());
        } else {
          mapNames.put(f.getName(), f.getName());
        }
      }
      else
      {
        mapNames.put(f.getName(), f.getName());
      }
    }
    return mapNames;
  }
  
  private Map<String, Method> getSetterMethod(Class<?> clazz)
    throws Exception
  {
    Map<String, Method> mapMethod = Maps.newHashMap();
    Collection<Field> fList = this.mapAllDBField.values();
    PropertyDescriptor[] lPropDesc = Introspector.getBeanInfo(clazz, 1).getPropertyDescriptors();
    Method setMethod;
    for (Field f : fList)
    {
      PropertyDescriptor[] arrayOfPropertyDescriptor1;
      int j = (arrayOfPropertyDescriptor1 = lPropDesc).length;
      for (int i = 0; i < j; i++)
      {
        PropertyDescriptor aLPropDesc = arrayOfPropertyDescriptor1[i];
        if (aLPropDesc.getName().equalsIgnoreCase(f.getName()))
        {
          setMethod = aLPropDesc.getWriteMethod();
          mapMethod.put(f.getName(), setMethod);
          break;
        }
      }
    }

    for(Field f : fList)
    {
        Method setterMethod = (Method)mapMethod.get(f.getName());
        if(setterMethod == null)
        {
            String setFunName = "";
            if(f.isAnnotationPresent(com.bj58.ecdata.dao.annotation.Column.class))
            {
                Column column = (Column)f.getAnnotation(com.bj58.ecdata.dao.annotation.Column.class);
                if(!column.setFuncName().equalsIgnoreCase("setField"))
                    setFunName = column.setFuncName();
                else
                    setFunName = (new StringBuilder("set")).append(f.getName().substring(0, 1).toUpperCase()).append(f.getName().substring(1)).toString();
            } else
            {
                setFunName = (new StringBuilder("set")).append(f.getName().substring(0, 1).toUpperCase()).append(f.getName().substring(1)).toString();
            }
            Method amethod[];
            int l = (amethod = clazz.getMethods()).length;
            for(int k = 0; k < l; k++)
            {
                Method m = amethod[k];
                if(!m.getName().equals(setFunName))
                    continue;
                setterMethod = m;
                break;
            }

            mapMethod.put(f.getName(), setterMethod);
        }
    }

    for(Iterator<Field> iterator2 = fList.iterator(); iterator2.hasNext();)
    {
        Field f = (Field)iterator2.next();
        Method setterMethod = (Method)mapMethod.get(f.getName());
        if(setterMethod == null)
            throw new Exception((new StringBuilder("can't find set method field:")).append(f.getName()).append("  class:").append(clazz.getName()).toString());
    }

    return mapMethod;
}

  
  private Map<String, Method> getGetterMethod(Class<?> clazz)
    throws Exception
  {
    Map<String, Method> mapMethod = Maps.newHashMap();
    Collection<Field> fList = this.mapAllDBField.values();
    PropertyDescriptor[] lPropDesc = Introspector.getBeanInfo(clazz, 1).getPropertyDescriptors();
    Method getMethod;
    for (Field f : fList)
    {
      PropertyDescriptor[] arrayOfPropertyDescriptor1;
      int j = (arrayOfPropertyDescriptor1 = lPropDesc).length;
      for (int i = 0; i < j; i++)
      {
        PropertyDescriptor aLPropDesc = arrayOfPropertyDescriptor1[i];
        if (aLPropDesc.getName().equalsIgnoreCase(f.getName()))
        {
          getMethod = aLPropDesc.getReadMethod();
          mapMethod.put(f.getName(), getMethod);
          break;
        }
      }
    }
    for (Field f : fList)
    {
      Method getterMethod = (Method)mapMethod.get(f.getName());
      String getFunName = "";
      if (f.isAnnotationPresent(Column.class))
      {
        Column column = (Column)f.getAnnotation(Column.class);
        if (!column.getFuncName().equalsIgnoreCase("getField")) {
          getFunName = column.getFuncName();
        } else {
          getFunName = "get" + f.getName().substring(0, 1).toUpperCase() + f.getName().substring(1);
        }
      }
      else
      {
        getFunName = "get" + f.getName().substring(0, 1).toUpperCase() + f.getName().substring(1);
      }
      Method amethod[];
      int l = (amethod = clazz.getMethods()).length;
      for(int k = 0; k < l; k++)
      {
          Method m = amethod[k];
          if(!m.getName().equals(getFunName))
              continue;
          getterMethod = m;
          break;
      }
      mapMethod.put(f.getName(), getterMethod);
    }
    for (Field f : fList)
    {
      Method getterMethod = (Method)mapMethod.get(f.getName());
      if (getterMethod == null) {
        throw new Exception("can't find get method field:" + f.getName() + "  class:" + clazz.getName());
      }
    }
    return mapMethod;
  }
  
  private ProcedureName getProc(Class<?> clazz)
  {
    return (ProcedureName)clazz.getAnnotation(ProcedureName.class);
  }
  
  private Map<String, Field> getIdentityFields(Class<?> clazz)
  {
    Map<String, Field> mapField = Maps.newHashMap();
    Field[] fields = clazz.getDeclaredFields();
    Field[] arrayOfField1;
    int j = (arrayOfField1 = fields).length;
    for (int i = 0; i < j; i++)
    {
      Field f = arrayOfField1[i];
      if (f.isAnnotationPresent(Id.class))
      {
        Id id = (Id)f.getAnnotation(Id.class);
        if ((!id.insertable()) && (!id.updatable())) {
          mapField.put(f.getName(), f);
        }
      }
    }
    return mapField;
  }
  
  private Map<String, Field> getTableRenameFields(Class<?> clazz)
  {
    Map<String, Field> mapTableRenamefields = Maps.newHashMap();
    Field[] fields = clazz.getDeclaredFields();
    Field[] arrayOfField1;
    int j = (arrayOfField1 = fields).length;
    for (int i = 0; i < j; i++)
    {
      Field f = arrayOfField1[i];
      if (f.isAnnotationPresent(TableRename.class)) {
        mapTableRenamefields.put(f.getName(), f);
      }
    }
    return mapTableRenamefields;
  }
  
  private Map<String, Method> getTableRenameSetterMethod(Class<?> clazz)
    throws Exception
  {
    Map<String, Method> mapMethod = Maps.newHashMap();
    Collection<Field> fList = this.mapTableRenameField.values();
    PropertyDescriptor[] lPropDesc = Introspector.getBeanInfo(clazz, 1).getPropertyDescriptors();
    Method setMethod;
    for (Field f : fList)
    {
      PropertyDescriptor[] arrayOfPropertyDescriptor1;
      int j = (arrayOfPropertyDescriptor1 = lPropDesc).length;
      for (int i = 0; i < j; i++)
      {
        PropertyDescriptor aLPropDesc = arrayOfPropertyDescriptor1[i];
        if (aLPropDesc.getName().equalsIgnoreCase(f.getName()))
        {
          setMethod = aLPropDesc.getWriteMethod();
          mapMethod.put(f.getName(), setMethod);
          break;
        }
      }
    }
    for (Field f : fList)
    {
      Method setterMethod = (Method)mapMethod.get(f.getName());
      if (setterMethod == null)
      {
        String setFunName = "";
        if (f.isAnnotationPresent(Column.class))
        {
          Column column = (Column)f.getAnnotation(Column.class);
          if (!column.setFuncName().equalsIgnoreCase("setField")) {
            setFunName = column.setFuncName();
          } else {
            setFunName = "set" + f.getName().substring(0, 1).toUpperCase() + f.getName().substring(1);
          }
        }
        else
        {
          setFunName = "set" + f.getName().substring(0, 1).toUpperCase() + f.getName().substring(1);
        }
        Method arrayOfMethod[];
        int l = (arrayOfMethod = clazz.getMethods()).length;
        for(int k = 0; k < l; k++)
        {
            Method m = arrayOfMethod[k];
            if(!m.getName().equals(setFunName))
                continue;
            setterMethod = m;
            break;
        }

        mapMethod.put(f.getName(), setterMethod);
    }
    }
    for (Field f : fList)
    {
      Method setterMethod = (Method)mapMethod.get(f.getName());
      if (setterMethod == null) {
        throw new Exception("can't find set method field:" + f.getName() + "  class:" + clazz.getName());
      }
    }
    return mapMethod;
  }
  
  private Map<String, Method> getTableRenameGetterMethod(Class<?> clazz)
    throws Exception
  {
    Map<String, Method> mapMethod = Maps.newHashMap();
    Collection<Field> fList = this.mapTableRenameField.values();
    PropertyDescriptor[] lPropDesc = Introspector.getBeanInfo(clazz, 1).getPropertyDescriptors();
    Method getMethod;
    for (Field f : fList)
    {
      PropertyDescriptor[] arrayOfPropertyDescriptor1;
      int j = (arrayOfPropertyDescriptor1 = lPropDesc).length;
      for (int i = 0; i < j; i++)
      {
        PropertyDescriptor aLPropDesc = arrayOfPropertyDescriptor1[i];
        if (aLPropDesc.getName().equalsIgnoreCase(f.getName()))
        {
          getMethod = aLPropDesc.getReadMethod();
          mapMethod.put(f.getName(), getMethod);
          break;
        }
      }
    }
    for (Field f : fList)
    {
      Method getterMethod = (Method)mapMethod.get(f.getName());
      String getFunName = "";
      if (f.isAnnotationPresent(Column.class))
      {
        Column column = (Column)f.getAnnotation(Column.class);
        if (!column.getFuncName().equalsIgnoreCase("getField")) {
          getFunName = column.getFuncName();
        } else {
          getFunName = "get" + f.getName().substring(0, 1).toUpperCase() + f.getName().substring(1);
        }
      }
      else
      {
        getFunName = "get" + f.getName().substring(0, 1).toUpperCase() + f.getName().substring(1);
      }
      Method[] arrayOfMethod;
      int l = (arrayOfMethod = clazz.getMethods()).length;
      for(int k = 0; k < l; k++)
      {
          Method m = arrayOfMethod[k];
          if(!m.getName().equals(getFunName))
              continue;
          getterMethod = m;
          break;
      }
      mapMethod.put(f.getName(), getterMethod);
    }
    for (Field f : fList)
    {
      Method getterMethod = (Method)mapMethod.get(f.getName());
      if (getterMethod == null) {
        throw new Exception("can't find get method field:" + f.getName() + "  class:" + clazz.getName());
      }
    }
    return mapMethod;
  }
  
  public Map<String, Field> getMapIDField()
  {
    return this.mapIDField;
  }
  
  public void setMapIDField(Map<String, Field> mapIDField)
  {
    this.mapIDField = mapIDField;
  }
  
  public Map<String, Field> getMapAllDBField()
  {
    return this.mapAllDBField;
  }
  
  public void setMapAllDBField(Map<String, Field> mapAllDBField)
  {
    this.mapAllDBField = mapAllDBField;
  }
  
  public Map<String, Field> getMapInsertableField()
  {
    return this.mapInsertableField;
  }
  
  public void setMapInsertableField(Map<String, Field> mapInsertableField)
  {
    this.mapInsertableField = mapInsertableField;
  }
  
  public Map<String, Field> getMapUpdatableField()
  {
    return this.mapUpdatableField;
  }
  
  public void setMapUpdatableField(Map<String, Field> mapUpdatableField)
  {
    this.mapUpdatableField = mapUpdatableField;
  }
  
  public Map<String, String> getMapDBColumnName()
  {
    return this.mapDBColumnName;
  }
  
  public void setMapDBColumnName(Map<String, String> mapDBColumnName)
  {
    this.mapDBColumnName = mapDBColumnName;
  }
  
  public Map<String, Method> getMapSetMethod()
  {
    return this.mapSetMethod;
  }
  
  public void setMapSetMethod(Map<String, Method> mapSetMethod)
  {
    this.mapSetMethod = mapSetMethod;
  }
  
  public Map<String, Method> getMapGetMethod()
  {
    return this.mapGetMethod;
  }
  
  public void setMapGetMethod(Map<String, Method> mapGetMethod)
  {
    this.mapGetMethod = mapGetMethod;
  }
  
  public String getTableName()
  {
    return this.tableName;
  }
  
  public void setTableName(String tableName)
  {
    this.tableName = tableName;
  }
  
  public void setProcdure(ProcedureName procdure)
  {
    this.procdure = procdure;
  }
  
  public ProcedureName getProcdure()
  {
    return this.procdure;
  }
  
  public void setMapIdentityField(Map<String, Field> mapIdentityField)
  {
    this.mapIdentityField = mapIdentityField;
  }
  
  public Map<String, Field> getMapIdentityField()
  {
    return this.mapIdentityField;
  }
  
  public Map<String, Field> getMapTableRenameField()
  {
    return this.mapTableRenameField;
  }
  
  public void setMapTableRenameField(Map<String, Field> mapTableRenameField)
  {
    this.mapTableRenameField = mapTableRenameField;
  }
  
  public Map<String, Method> getMapTableRenameSetMethod()
  {
    return this.mapTableRenameSetMethod;
  }
  
  public void setMapTableRenameSetMethod(Map<String, Method> mapTableRenameSetMethod)
  {
    this.mapTableRenameSetMethod = mapTableRenameSetMethod;
  }
  
  public Map<String, Method> getMapTableRenameGetMethod()
  {
    return this.mapTableRenameGetMethod;
  }
  
  public void setMapTableRenameGetMethod(Map<String, Method> mapTableRenameGetMethod)
  {
    this.mapTableRenameGetMethod = mapTableRenameGetMethod;
  }
}
